package com.leyou.search.service.impl;

import com.leyou.item.client.ItemClient;
import com.leyou.search.dto.SearchRequest;
import com.leyou.search.entity.Goods;
import com.leyou.search.repository.GoodsRepository;
import com.leyou.search.service.SearchService;
import com.leyou.starter.elastic.entity.PageInfo;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.Nested;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private ItemClient itemClient;

    @Override
    public Mono<List<String>> getSuggest(String key) {
        return this.goodsRepository.suggestBySingleField("suggestion", key);
    }

    public QueryBuilder getQueryBuilder(SearchRequest searchRequest) {

        String key = searchRequest.getKey();

        //创建查询构造条件
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        //给查询构造条件添加基础查询条件
        boolQueryBuilder.must(QueryBuilders.matchQuery("title", key)
                .operator(Operator.AND));

        //获取过滤条件
        Map<String, String> filters = searchRequest.getFilters();

        //过滤条件不为空，为查询构造条件循环添加过滤条件
        if(!CollectionUtils.isEmpty(filters)){
            filters.entrySet().forEach(entry->{
                String entryKey = entry.getKey();
                String entryValue = entry.getValue();

                if("分类".equals(entryKey)){
                    entryKey = "categoryId";
                    boolQueryBuilder.filter(QueryBuilders.termQuery(entryKey,entryValue));
                }else if("品牌".equals(entryKey)){
                    entryKey = "brandId";
                    boolQueryBuilder.filter(QueryBuilders.termQuery(entryKey,entryValue));
                }else {

                }

            });
        }

        return boolQueryBuilder;
    }

    @Override
    public Mono<PageInfo<Goods>> listData(SearchRequest searchRequest) {
        //创建查询条件
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        //添加查询高亮字段
        HighlightBuilder highlightBuilder = new HighlightBuilder();

        highlightBuilder.preTags("<em>");
        highlightBuilder.postTags("</em>");
        highlightBuilder.field("title");

        //获取接受的查询条件与筛选条件
        QueryBuilder queryBuilder = getQueryBuilder(searchRequest);

        //如果查询条件为空跳过基础查询与高亮字段条件
        if(!StringUtils.isEmpty(searchRequest.getKey())){
            sourceBuilder.query(queryBuilder);
            sourceBuilder.highlighter(highlightBuilder);
        }

        //健壮性检测，当为传入查询页码时默认查询第一页
        int page = searchRequest.getPage() == null ? 1 : searchRequest.getPage();

        //准备分页查询条件
        Integer rows = searchRequest.getRows();
        int from = (page-1)*rows;
        sourceBuilder.from(from);
        sourceBuilder.size(rows);

        //准备排序条件
        String sort = searchRequest.getSortBy();
        if(StringUtils.isNoneBlank(sort)){
            sourceBuilder.sort(SortBuilders.fieldSort(sort)
                    .order(searchRequest.getDesc()? SortOrder.DESC:SortOrder.ASC));
        }

        return this.goodsRepository.queryBySourceBuilderForPageHighlight(sourceBuilder);
    }

    @Override
    public Mono<Map<String, List<?>>> listFilter(SearchRequest searchRequest) {
        //创建查询条件存储器
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        //添加Query查询条件
        sourceBuilder.query(getQueryBuilder(searchRequest));

        //添加brandId聚合条件
        sourceBuilder.aggregation(AggregationBuilders.terms("brandAgg").field("brandId").size(20));

        //添加categoryId聚合条件
        sourceBuilder.aggregation(AggregationBuilders.terms("categoryAgg").field("categoryId").size(20));

        //添加商品参数聚合条件
        sourceBuilder.aggregation(
                AggregationBuilders.nested("specAgg", "specs")
                        //添加商品参数名称聚合条件
                        .subAggregation(AggregationBuilders.terms("nameAgg").field("specs.name")
                                //添加商品参数值聚合条件
                                .subAggregation(AggregationBuilders.terms("valueAgg")
                                        .field("specs.value").size(30))));

        //使用查询条件查询elasticsearch中的数据
        Mono<Aggregations> aggregationsMono = this.goodsRepository
                .aggregationBySourceBuilder(sourceBuilder);

        //返回数据
        return aggregationsMono.map(aggregations -> {
            //过滤条件的封装集合
            Map<String,List<?>> resultMap = new HashMap<>();

            //解析品牌聚合
            Terms brandAgg = aggregations.get("brandAgg");

            //获取品牌id集合
            List<Long> brandIds = brandAgg
                    .getBuckets()
                    .stream()
                    .map(bucket -> ((Terms.Bucket) bucket).getKeyAsNumber())
                    .map(Number::longValue)
                    .collect(Collectors.toList());

//            List<BrandDTO> brandDTOS = this.itemClient.queryBrandByIds(brandIds);

            //根据品牌id集合查询对应品牌的集合
            if (!CollectionUtils.isEmpty(brandIds)) {
                resultMap.put("品牌",this.itemClient.queryBrandByIds(brandIds));
            }

            //解析分类聚合
            Terms categoryAgg = aggregations.get("categoryAgg");

            //获取分类id集合
            List<Long> categoryIds = categoryAgg
                    .getBuckets()
                    .stream()
                    .map(bucket -> ((Terms.Bucket) bucket).getKeyAsNumber())
                    .map(Number::longValue)
                    .collect(Collectors.toList());

//            List<CategoryDTO> categoryDTOS = this.itemClient.queryCategoryByIds(categoryIds);

            //根据获取到的分类id集合，查询分类集合
            if (!CollectionUtils.isEmpty(categoryIds)) {
                resultMap.put("分类",this.itemClient.queryCategoryByIds(categoryIds));
            }

            //解析规格参数的nested聚合
            Nested specAgg = aggregations.get("specAgg");

            //解析规格参数名聚合
            Terms nameAgg = specAgg.getAggregations().get("nameAgg");

            //遍历名称分桶
            nameAgg.getBuckets().forEach(bucket->{
                //获取规格参数名
                String keyAsString = ((Terms.Bucket) bucket).getKeyAsString();

                //解析规格参数值聚合
                Terms valueAgg = ((Terms.Bucket) bucket).getAggregations().get("valueAgg");

                //把名称对应的值封装成数组
                List<String> list = valueAgg.getBuckets()
                        .stream()
                        .map(
                                valueBucket -> ((Terms.Bucket) valueBucket).getKeyAsString())
                        .collect(Collectors.toList());

                resultMap.put(keyAsString, list);
            });


            return resultMap;
        });
    }
}
